#ifndef MATRIX_SYSTEM_HPP
#define MATRIX_SYSTEM_HPP

#include "Math.hpp"

#include <stack>

namespace Data {

#define GetMatrixA Data::MatrixStack::getMatrixStack()

enum class MatrixMode { MODELVIEW_MATRIX, CUSTOM_MATRIX, PERSPECTIVE_MATRIX };

class MatrixStack {
public:
  static MatrixStack &getMatrixStack();

  // TODO: The push and pop need to fit with object hierarchy
  void pushMatrix();
  void popMatrix();

  void translate(const glm::vec3 &position);
  // the second parameter is radiance not degree
  void rotate(const glm::vec3 &axis, float d);
  void scale(const glm::vec3 &scale);
  void shear(const glm::mat4 &matrix);

  void setCurrentIdentity();
  void setIdentityByMode(MatrixMode mode);
  void setMatrixMode(MatrixMode mode) { _mat_mode = mode; }

  void setCurrentMatrix(const glm::mat4 &m);
  void setTopMatrixByMode(const glm::mat4 &m, MatrixMode mode);
  glm::mat4 getCurrentMatrix();
  glm::mat4 getCurrentMatrixByMode(MatrixMode mode);

  void multiMatrix(const glm::mat4 &matrix);

  glm::mat4 getMVP();
  glm::mat4 getMV();

  MatrixStack(const MatrixStack &) = delete;
  MatrixStack(MatrixStack &&) = delete;

private:
  MatrixStack();

  MatrixMode _mat_mode;
  std::stack<glm::mat4> _modelView_mat_stack;
  std::stack<glm::mat4> _custom_mat_stack;
  std::stack<glm::mat4> _perspective_mat_stack;
};
} // namespace Data

#endif